package com.lill.common.component.transaction;

import java.util.Iterator;
import java.util.Map;
import java.util.Stack;

import org.apache.commons.lang.ArrayUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import com.lill.common.component.util.StringUtils;

@Aspect
@Configuration
public class CommonTransactionAspect {
	
	protected static Logger logger = LoggerFactory.getLogger(CommonTransactionAspect.class);
	
	@Autowired
    private ApplicationContext applicationContext;
	
	@Pointcut("@annotation(com.lill.common.component.transaction.CommonTransaction)")
	public void commonTransaction() {
	}
	
	ThreadLocal<String> transactionLocal = new ThreadLocal<>();
	
	@Around(value = "commonTransaction()&&@annotation(commonTransaction)")
	public Object multiDataSourceTransaction(ProceedingJoinPoint joinPoint,CommonTransaction commonTransaction) {
		Stack<Object> dstms = new Stack<Object>();
		Stack<TransactionStatus> tss = new Stack<TransactionStatus>();
		try {
			openInitTransaction(dstms, tss,commonTransaction);
			return joinPoint.proceed();
		} catch (Throwable e) {
			try {
				rollback(dstms, tss);
			} catch (Exception er) {
				logger.error(er.getMessage(), er);
			}
			logger.error(String.format("MultiTransactionalAspect, method:%s-%s occors error:",
					joinPoint.getTarget().getClass().getSimpleName(), joinPoint.getSignature().getName()), e);
			return null;
		}finally{
			commit(dstms, tss);
		}
	}
	
	/**
	 * 开启事务处理方法
	 */
	private void openInitTransaction(Stack<Object> dstms,Stack<TransactionStatus> tss,CommonTransaction multiTransactional) {
		String[] tmns = multiTransactional.value();
		Map<String, DataSourceTransactionManager> managerMap=applicationContext.getBeansOfType(DataSourceTransactionManager.class);
		if(null!=managerMap&&!managerMap.isEmpty()){
			Iterator<String> itKeys=managerMap.keySet().iterator();
			String key = null;
			TransactionStatus ts = null;
			DataSourceTransactionManager tm = null;
			if (ArrayUtils.isEmpty(tmns)) {
				while (itKeys.hasNext()) {
					key = itKeys.next();
					if(null!=key){
						if(StringUtils.isNotEmpty(transactionLocal.get())){
							if(!transactionLocal.get().contains(key)){
								transactionLocal.set(transactionLocal.get()+","+key);
							}else{
								continue;
							}
						}else{
							transactionLocal.set(key);
						}
						tm = managerMap.get(key);
						ts = tm.getTransaction(new DefaultTransactionDefinition());
						tss.push(ts);
						dstms.push(tm);
					}
				}
			}else{
				for (int i = 0; i < tmns.length; i++) {
					tm = managerMap.get(tmns[i]);
					if(null!=tm){
						if(StringUtils.isNotEmpty(transactionLocal.get())){
							if(!transactionLocal.get().contains(tmns[i])){
								transactionLocal.set(transactionLocal.get()+","+tmns[i]);
							}else{
								continue;
							}
						}else{
							transactionLocal.set(tmns[i]);
						}
						ts = tm.getTransaction(new DefaultTransactionDefinition());
						tss.push(ts);
						dstms.push(tm);
					}
				}
			}
		}
	}

	/**
	 * 提交处理方法
	 */
	private void commit(Stack<Object> dstms,Stack<TransactionStatus> tss) {
		DataSourceTransactionManager tm = null;
		while (!dstms.isEmpty()) {
			Object obj=dstms.pop();
			if(obj instanceof DataSourceTransactionManager){
				tm=(DataSourceTransactionManager) obj;
				tm.commit(tss.pop());
			}
		}
	}
	
	/**
	 * 回滚处理方法
	 */
	private void rollback(Stack<Object> dstms,Stack<TransactionStatus> tss) {
		DataSourceTransactionManager tm = null;
		while (!dstms.isEmpty()) {
			Object obj=dstms.pop();
			if(obj instanceof DataSourceTransactionManager){
				tm=(DataSourceTransactionManager) obj;
				tm.rollback(tss.pop());
			}
		}
	}
	
}
